#include <ilcplex/ilocplex.h>
#include "../simulate.h"
#include "../parse.h"

ILOSTLBEGIN
typedef IloArray<IloNumArray> NumMatrix;
typedef IloArray<IloNumVarArray> NumVarMatrix;

int DFF_Num;
int tuning_factor = 2;
vector<resMap*> Mymap;
const int MAX_LINE_LENGTH = 65536;
char line_buffer[MAX_LINE_LENGTH];

int read_resMap(ifstream &in, int **containList)
{
        char *s;
        string tmp;
        resMap *Rm;
        int i = 0, cnt = 0;
        
        while(in.getline(line_buffer, MAX_LINE_LENGTH))
        {
                //cout<<"HEHE"<<endl;
                //cout<<line_buffer<<endl;
                switch(i){
                        case 0:
                                Rm  = new resMap;
                                tmp = ""; cnt = 0;
                                while(line_buffer[cnt++] != '#'){}
                                cnt++;
                                //cout<<"pos is "<<cnt<<endl;
                                //cout<<line_buffer[cnt]<<endl;
                                while(line_buffer[cnt] != '\t')
                                        tmp += line_buffer[cnt++];
                                Rm->DFF_id = atoi(tmp.c_str());
                                //cout<<"pos is "<<cnt<<endl;
                                //cout<<tmp<<endl;
                                cnt++;
                                tmp = "";
                                while(line_buffer[cnt] != '\t')
                                        tmp += line_buffer[cnt++];
                                Rm->P0 = atof(tmp.c_str());
                                //cout<<"pos is "<<cnt<<endl;
                                //cout<<tmp<<endl;
                                cnt++;
                                tmp = "";
                                while(line_buffer[cnt] != '\0')
                                        tmp += line_buffer[cnt++];
                                Rm->P1 = atof(tmp.c_str());
                                //cout<<"pos is "<<cnt<<endl;
                                //cout<<tmp<<endl;
                                i++;
                                break;
                        case 1:
                                i++;
                                break;
                        case 2:
                                s = strtok(line_buffer,"\t");
                                if(s != NULL){
                                        Rm->R0.insert(atoi(s));
                                        containList[atoi(s)][Rm->DFF_id] = 1;
                                }
                                while(s != NULL)
                                {
                                        s = strtok(NULL,"\t\n");
                                        if(s == NULL)
                                                break;
                                        Rm->R0.insert(atoi(s));
                                        containList[atoi(s)][Rm->DFF_id] = 1;
                                }
                                i++;
                                break;
                        case 3:
                                i++;
                                break;
                        case 4:
                                s = strtok(line_buffer,"\t");
                                if(s != NULL){
                                        Rm->R1.insert(atoi(s));
                                        containList[atoi(s)][Rm->DFF_id] = 1;
                                }
                                while(s != NULL)
                                {
                                        s = strtok(NULL,"\t\n");
                                        if(s == NULL)
                                                break;
                                        Rm->R1.insert(atoi(s));
                                        containList[atoi(s)][Rm->DFF_id] = 1;
                                }
                                Mymap.push_back(Rm);
                                i = 0;
                                break;
                        default:
                                return 1;
                }
        }
        
        in.close();
        return 0;
}

int main(int argc, char** argv) {
        int i,j,k;
        IloEnv env;
        time_t start,end;
        time_t start1,end1;
        //time (&start);
        
        if (argc != 3){
                cout<<"Usage: ./solver [restore_map] [trace_size]"<<endl;
                exit(0);
        }
        
        ifstream in(argv[1]);
        in.getline(line_buffer, MAX_LINE_LENGTH);
        cout<<"Number of DFFs is "<<atoi(line_buffer)<<endl;
        DFF_Num = atoi(line_buffer);

        int **containList;
        containList = new int*[DFF_Num];
        for(i = 0; i < DFF_Num; i++)
        {
                containList[i] = new int[DFF_Num];
                memset(containList[i], -1, sizeof(int) * DFF_Num);
        }
        
        read_resMap(in,containList);
        
        //define variables.........................
        cout<<"Check here! Map size is "<<Mymap.size()<<endl;        
        IloModel model(env);
        //time (&start1);
        
        /*define decision variables*/
        //long mapSize = Mymap.size();
        //double * P0 = new double[mapSize];
        //double * P1 = new double[mapSize];
        //IloNumArray P0(env, mapSize, 0.0, +IloInfinity, ILOFLOAT);
        //IloNumArray P1(env, mapSize, 0.0, +IloInfinity, ILOFLOAT);

        //define Zi
        IloNumVarArray Zi(env, DFF_Num, 0, 1, ILOINT);
        //define Xij, i is list id, j is list content 
        NumVarMatrix Xij(env, DFF_Num);
        for (i = 0; i < DFF_Num; i++) 
                Xij[i] = IloNumVarArray(env, DFF_Num, 0, 1, ILOINT);
        
        try{                
                set<int>::iterator it_set;
                set<int>::iterator it_set1;
                vector<resMap*>::iterator it_map;
                for(it_map = Mymap.begin();it_map != Mymap.end();it_map++)
                {
                        for(it_set = (*it_map)->R0.begin(); it_set != (*it_map)->R0.end(); ){
                                
                                it_set1 = (*it_map)->R1.find(*it_set);
                                if(it_set1 != (*it_map)->R1.end()){
                                        (*it_map)->Overlap.insert(*it_set1);
                                        (*it_map)->R0.erase(it_set);
                                        (*it_map)->R1.erase(it_set1);
                                        continue;
                                }
                                it_set++;
                        }
                }
                
                //for check
                /*
                for(it_map = Mymap.begin();it_map != Mymap.end();it_map++)
                {
                        cout<<"DFF # "<<(*it_map)->DFF_id<<"\t"<<(*it_map)->P0<<"\t"<<(*it_map)->P1<<endl;
                        cout<<"restored DFF from 0"<<endl;
                        for(it_set = (*it_map)->R0.begin(); it_set != (*it_map)->R0.end(); it_set++)
                                cout<<*it_set<<"\t";
                        cout<<endl;
                        
                        cout<<"restored DFF from 1"<<endl;
                        for(it_set = (*it_map)->R1.begin(); it_set != (*it_map)->R1.end(); it_set++)
                                cout<<*it_set<<"\t";
                        cout<<endl;
                        
                        cout<<"Overlapped: "<<endl;
                        for(it_set = (*it_map)->Overlap.begin(); it_set != (*it_map)->Overlap.end(); it_set++)
                                cout<<*it_set<<"\t";
                        cout<<endl;
                }
                */
                
                //add objective expression
                IloExpr L0(env);
                IloExpr L1(env);
                IloExpr Visibility(env);
        
                //------------------------------------------------adding constraints-------------------------------------------------
                //X0ij<=Zi,X1ij<=Zi,check overlap, better extract the
                //overlapp out
                //add objective at the same time

                for(it_map = Mymap.begin();it_map != Mymap.end();it_map++)
                {
                        
                        for(it_set = (*it_map)->R0.begin(); it_set != (*it_map)->R0.end(); it_set++){
                                model.add(Xij[(*it_map)->DFF_id][*it_set] <= Zi[(*it_map)->DFF_id]);
                                L0 += Xij[(*it_map)->DFF_id][*it_set];
                        }
                        
                        for(it_set = (*it_map)->R1.begin(); it_set != (*it_map)->R1.end(); it_set++)
                        {
                                model.add(Xij[(*it_map)->DFF_id][*it_set] <= Zi[(*it_map)->DFF_id]);
                                L1 += Xij[(*it_map)->DFF_id][*it_set];
                        }
                        
                        Visibility += (*it_map)->P0 * L0 + (*it_map)->P1 * L1;
                        for(it_set = (*it_map)->Overlap.begin(); it_set != (*it_map)->Overlap.end(); it_set++)
                        {
                                model.add(Xij[(*it_map)->DFF_id][*it_set] <= Zi[(*it_map)->DFF_id]);
                                Visibility += Xij[(*it_map)->DFF_id][*it_set];
                        }
                }
                
                IloObjective obj = IloMaximize(env,Visibility);
                model.add(obj);
                Visibility.end();
                
                int trace_size = atoi(argv[2]);
                //adding constraint sum Zi == trace_size
                IloExpr expr1(env,0);
                for (i = 0; i < DFF_Num; i++)
                        expr1 += Zi[i];
                model.add(expr1 == trace_size);

                //adding constraint sum Xji <= 2
                for (j = 0; j < DFF_Num; j++) {
                        IloExpr expr(env,0);
                        for (i = 0; i < DFF_Num; i++) {
                                if(containList[j][i] == 1)
                                        expr += Xij[i][j];
                        }
                        model.add(expr <= tuning_factor);
                }
                
                //----------------------------------------------------solve and optimize-----------------------------------------------------
                IloCplex cp(model);
                cp.setParam(cp.EpGap,0.20);
                cp.setParam(cp.TiLim,60);
                cp.setParam(cp.TreLim,6e9);
                cp.setParam(cp.MemoryEmphasis,true);
                cp.extract(model);
                
                cp.solve();
                
                //ofstream logfile("my.log");
                //cp.setOut(logfile);		  
                
                cout << std::endl << "Best Optimal value: " << cp.getBestObjValue() << std::endl;		    
                cout << std::endl << "Optimal value: " << cp.getValue(obj) << std::endl;
                cout << cp.getStatus() << std::endl;
                
                string filename(argv[1]);
                int cnt = 0;
                for(i = 0; i < strlen(filename.c_str()); i++)
                {
                        if(filename.c_str()[i] == 'r')
                                cnt++;
                        if(cnt == 2)
                                break;
                }
                filename.erase(filename.begin() + i - 1, filename.end());
                
                filename += ".result";
                cout<<"output file name is "<<filename<<endl;

                ofstream out(filename.c_str());
                cout<<"Selected DFFs are "<<endl;
                for (i = 0; i < DFF_Num; i++) 
                        if(cp.getValue(Zi[i]) == 1)
                        {
                                cout<<i<<endl;
                                out<<i<<endl;
                        }
                out.close();
                //----------------------------------------------------print out solution-----------------------------------------------------
                /*
                Circuit *C;
                string filename(argv[1]);
                int cnt = 0;
                for(i = 0; i < strlen(filename.c_str()); i++)
                {
                        if(filename.c_str()[i] == 'r')
                                cnt++;
                        if(cnt == 2)
                                break;
                }
                filename.erase(filename.begin() + i - 2);
                cout<<"benchmark name is" << filename << endl;
                char *s = new char[strlen(filename.c_str())];
                strcpy(s,filename.c_str()); 
                //parse(s);
                
                Simulate *sim = new Simulate(C);
                sim->tracedSigs.clear();
                
                cout<<"Selected DFFs are "<<endl;
                for (i = 0; i < DFF_Num; i++) 
                        if(cp.getValue(Zi[i]) == 1)
                        {
                                sim->Ci->getFlipflop(i)->getOutput(0)->setTraced(true);
                                sim->tracedSigs.push_back(sim->Ci->getFlipflop(i)->getOutput(0));
                                cout<<i<<"\t";
                        }
                cout<<endl;
                
                //cout<<"# VN is "<<sim->computVN()<<endl;
                */
                cp.end();
                model.end();
        }
        catch (IloException& ex) {
                env.out() << "Error: " << ex << std::endl;
        }
        
        env.end();        
        return 0;
}



